/*
 * RA07.c
 *
 *  Created on: 2022年1月12日
 *      Author: jiesh
 */


#include "RA07.h"
#include "tos_at.h"
#include "lora_module_wrapper.h"
#include "fsl_debug_console.h"
#include <stdio.h>


#define DELAY(ms) tos_task_delay(ms)


typedef enum RA07RebootMode
{
	RA07_REBOOT_NOW = 0,
	RA07_REBOOT_FINISHED_SEND = 1,
	RA07_REBOOT_BOOTLOADER = 2
} RA07RebootMode;


typedef enum RA07Class
{
	RA07_CLASS_A = 0,
	RA07_CLASS_B = 1,
	RA07_CLASS_C = 2
} RA07Class;


typedef enum RA07ADR
{
	RA07_ADR_DISABLE = 0,
	RA07_ADR_ENABLE = 1
} RA07ADR;


typedef enum RA07DataRate
{
	RA07_DR_SF12 = 0,
	RA07_DR_SF11 = 1,
	RA07_DR_SF10 = 2,
	RA07_DR_SF9 = 3,  // factory value
	RA07_DR_SF8 = 4,
	RA07_DR_SF7 = 5,
} RA07DataRate;


typedef enum RA07JoinMode
{
	RA07_JOIN_MODE_OATT = 0,
	RA07_JOIN_MODE_ABP = 1,
} RA07JoinMode;


typedef enum RA07IDType
{
	RA07_ID_DEVADDR = 0,
	RA07_ID_DEVEUI = 1,
	RA07_ID_APPEUI = 2,
} RA07IDType;


typedef enum RA07KEYType {
	RA07_KEY_APPKEY,
	RA07_KEY_APPSKEY,
	RA07_KEY_NWKSKEY,
} RA07KEYType;


static char __num2hex(uint8_t num)
{
    if (num <= 0x9) {
        return num + '0';
    }

    if ((0xA <= num) && (num <= 0xF)) {
        return num - 0xA + 'A';
    }

    return (char)-1;
}


static void __hex2str(uint8_t *in, char *out, int len)
{
	int i = 0;

	for (i = 0; i < len; ++i) {
		out[i * 2] = __num2hex(in[i] >> 4);
		out[i * 2 + 1] = __num2hex(in[i] & 0x0F);
	}
	out[2 * len] = '\0';
}


static uint8_t __ascii2hex(char in) {
    if (('0' <= in) && (in <= '9')) {
        return in - '0';
    }
    if (('A' <= in) && (in <= 'F')) {
        return in - 'A' + 10;
    }
    if (('a' <= in) && (in <= 'f')) {
        return in - 'a' + 10;
    }
    return (uint8_t)-1;
}


static void __asciistr2hex(char *in, uint8_t *out, int len) {
    int i = 0;

    for (i = 0; i < len; i += 2) {
        out[i / 2] = (__ascii2hex(in[i]) << 4) + __ascii2hex(in[i + 1]);
    }
}


static int ra07_reboot(RA07RebootMode mode)
{
	int try = 0;
	at_echo_t echo;

	char cmd[16];
	snprintf(cmd, sizeof(cmd), "AT+IREBOOT=%d\r\n", mode);

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_class(RA07Class clazz)
{
	int try = 0;
	at_echo_t echo;

	char cmd[16];
	snprintf(cmd, sizeof(cmd), "AT+CCLASS=%d\r\n", clazz);

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_band(void)
{
	int try = 0;
	at_echo_t echo;

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, "AT+CFREQBANDMASK=0001\r\n");  // 0-7: 0001 8-15: 0002 ...
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_adr(RA07ADR adr)
{
	int try = 0;
	at_echo_t echo;

	char cmd[16];
	snprintf(cmd, sizeof(cmd), "AT+CADR=%d\r\n", adr);

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_uncomfirmed_repeat(int num)
{
	int try = 0;
	at_echo_t echo;

	char cmd[24];
	snprintf(cmd, sizeof(cmd), "AT+CNBTRIALS=0,%d\r\n", num);

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_data_rate(RA07DataRate dr)
{
	int try = 0;
	at_echo_t echo;

	char cmd[24];
	snprintf(cmd, sizeof(cmd), "AT+CDATARATE=%d\r\n", dr);

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_init(void)
{
    PRINTF("Init RA07 LoRa ...\n" );

    DELAY(1000);

    if (ra07_reboot(RA07_REBOOT_NOW) != 0) {
    	PRINTF("RA07 reboot FAILED\n");
        return -1;
    }

    if (ra07_set_class(RA07_CLASS_A) != 0) {
    	PRINTF("RA07 set class FAILED\n");
        return -1;
    }

    if (ra07_set_band() != 0) {
    	PRINTF("RA07 set band FAILED\n");
        return -1;
    }

    if (ra07_set_adr(RA07_ADR_DISABLE) != 0) {
    	PRINTF("RA07 set adr FAILED\n");
        return -1;
    }

    if(ra07_set_uncomfirmed_repeat(1)!=0){
    	PRINTF("RA07 set repeat times for unconfirmed message FAILED\n");
        return -1;
    }
    DELAY(2000);
    PRINTF("Init RA07 LoRa done\n");

    ra07_set_data_rate(RA07_DR_SF12);

    return 0;
}


static int ra07_set_join_mode(RA07JoinMode mode)
{
	int try = 0;
	at_echo_t echo;

	char cmd[24];
	snprintf(cmd, sizeof(cmd), "AT+CJOINMODE=%d\r\n", mode);

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_id(RA07IDType id_type, const char * value)
{
	int try = 0;
	at_echo_t echo;

	char cmd[128];
	switch (id_type)
	{
	case RA07_ID_DEVADDR:
		snprintf(cmd, sizeof(cmd), "AT+CDEVADDR=%s\r\n", value);
		break;
	case RA07_ID_DEVEUI:
		snprintf(cmd, sizeof(cmd), "AT+CDEVEUI=%s\r\n", value);
		break;
	case RA07_ID_APPEUI:
		snprintf(cmd, sizeof(cmd), "AT+CAPPEUI=%s\r\n", value);
		break;
	default:
		return -1;
	}

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_set_key(RA07KEYType key_type, const char * value)
{
	int try = 0;
	at_echo_t echo;

	char cmd[128];
	switch (key_type)
	{
	case RA07_KEY_APPKEY:
		snprintf(cmd, sizeof(cmd), "AT+CAPPKEY=%s\r\n", value);
		break;
	case RA07_KEY_APPSKEY:
		snprintf(cmd, sizeof(cmd), "AT+CAPPSKEY=%s\r\n", value);
		break;
	case RA07_KEY_NWKSKEY:
		snprintf(cmd, sizeof(cmd), "AT+CNWKSKEY=%s\r\n", value);
		break;
	default:
		return -1;
	}

	tos_at_echo_create(&echo, NULL, 0, "OK");
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
			return 0;
		}
	}
	return -1;
}


static int ra07_join_otaa(const char * deveui, const char * appkey)
{
    int try = 0;
    at_echo_t echo;

    if (ra07_set_join_mode(RA07_JOIN_MODE_OATT) != 0) {
        printf("RA07 set join mode FAILED\n");
        return -1;
    }

    if (ra07_set_id(RA07_ID_DEVEUI, (char *)deveui) != 0) {
        printf("RA07 set deveui FAILED\n");
        return -1;
    }

    if (ra07_set_key(RA07_KEY_APPKEY, (char *)appkey) != 0) {
        printf("RA07 set appkey FAILED\n");
        return -1;
    }

    DELAY(2000);

    tos_at_echo_create(&echo, NULL, 0, "OK");
    while (try++ < 10) {
        tos_at_cmd_exec(&echo, 14000, "AT+CJOIN=1,0,10,8\r\n");
        if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
            return 0;
        }
    }
    return -1;
}


int ra07_join_abp(const char *deveui, const char *devaddr, const char *nwkskey, const char *appskey)
{
    int try = 0;
    at_echo_t echo;

    if (ra07_set_join_mode(RA07_JOIN_MODE_ABP) != 0) {
        printf("RA07 set join mode FAILED\n");
        return -1;
    }

    if (ra07_set_id(RA07_ID_DEVEUI, (char *)deveui) != 0) {
        printf("RA07 set deveui FAILED\n");
        return -1;
    }

    if (ra07_set_id(RA07_ID_DEVADDR, (char *)devaddr) != 0) {
        printf("RA07 set devaddr FAILED\n");
        return -1;
    }

    if (ra07_set_key(RA07_KEY_NWKSKEY, (char *)nwkskey) != 0) {
        printf("RA07 set nwkskey FAILED\n");
        return -1;
    }

    if (ra07_set_key(RA07_KEY_APPSKEY, (char *)appskey) != 0) {
        printf("RA07 set appskey FAILED\n");
        return -1;
    }

    DELAY(2000);

    tos_at_echo_create(&echo, NULL, 0, "OK");
    while (try++ < 10) {
        tos_at_cmd_exec(&echo, 14000, "AT+CJOIN=1,0,10,8\r\n");
        if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
            return 0;
        }
    }
    return -1;
}


static int ra07_send(const void *buf, size_t len)
{
    char *str_buf = NULL;

    str_buf = tos_mmheap_calloc(2 * len + 1, sizeof(char));
    if (!str_buf) {
        return -1;
    }
    __hex2str((uint8_t *)buf, str_buf, len);

    char cmd[128];
    at_echo_t echo;
    snprintf(cmd, sizeof(cmd), "AT+DTRX=1,5,%d,%s\r\n", len, str_buf);
    cmd[sizeof(cmd) - 1] = '\0';
    tos_mmheap_free(str_buf);
    tos_at_echo_fuzzy_matching_create(&echo, NULL, 0, "OK+RECV");
    tos_at_cmd_exec(&echo, 6000, cmd);
    if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
        return len;
    }
    return -1;
}


static int ra07_send_unconfirmed(const void *buf, size_t len)
{
    char *str_buf = NULL;

    str_buf = tos_mmheap_calloc(2 * len + 1, sizeof(char));
    if (!str_buf) {
        return -1;
    }
    __hex2str((uint8_t *)buf, str_buf, len);

    char cmd[128];
    at_echo_t echo;
    snprintf(cmd, sizeof(cmd), "AT+DTRX=0,2,%d,%s\r\n", len, str_buf);
    cmd[sizeof(cmd) - 1] = '\0';
    tos_mmheap_free(str_buf);
    tos_at_echo_fuzzy_matching_create(&echo, NULL, 0, "OK+SENT");
    tos_at_cmd_exec(&echo, 6000, cmd);
    if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
        return len;
    }
    return -1;
}


static int ra07_close(void)
{
    return 0;
}


static lora_module_t lora_module_ra07 = {
    .init             = ra07_init,
    .join_otaa        = ra07_join_otaa,
    .join_abp         = ra07_join_abp,
    .send             = ra07_send,
    .send_unconfirmed = ra07_send_unconfirmed,
    .close            = ra07_close,
    .recv_callback    = K_NULL,
};


static char incoming_data_buffer[128];
static uint8_t hex_stream[128];


static void ra07_incoming_data_process(void)
{
    int ret;
    uint8_t data;

    /*
    +CMSG: PORT: 8; RX: "12345678"
    RX:       prefix
    "12345678":    data content
    */

    while (1) {
        ret = tos_at_uart_read(&data, 1);
        if (data == '"') {
            break;
        }
    }

    ret = 0;

    memset(incoming_data_buffer, 0x00, sizeof(incoming_data_buffer));

    while (1) {
        tos_at_uart_read(&data, 1);
        if (data == '"') {
            break;
        }
		if(ret<128)
		{
			incoming_data_buffer[ret++] = data;
		}
		else
		{
			printf(" ERR:incomming data is big ,please give more space for incoming_data_buffer\n");
		}

    }

    printf("rhf76_incoming_data_process %d: %s\n", ret, incoming_data_buffer);

    __asciistr2hex(incoming_data_buffer, hex_stream, strlen(incoming_data_buffer));

    if (lora_module_ra07.recv_callback) {
    	lora_module_ra07.recv_callback(hex_stream, strlen(incoming_data_buffer) / 2);
    }
}


static at_event_t ra07_at_event[] = {
	{ "OK+RECV:", ra07_incoming_data_process }
};


int ra07_lora_init(hal_uart_port_t uart_port)
{
    if (tos_at_init(uart_port, ra07_at_event,
                        sizeof(ra07_at_event) / sizeof(ra07_at_event[0])) != 0) {
        return -1;
    }

    DELAY(1000);

    if (tos_lora_module_register(&lora_module_ra07) != 0) {
        return -1;
    }

    if (tos_lora_module_init() != 0) {
        return -1;
    }

    return 0;
}
